// 类需要用到的常量
// 进行中状态
const PENDING = 'pending'
// 成功状态
const FULFILLED = 'fulfilled'
// 失败状态
const REJECTED = 'rejected'

/**
 *
 * 解析promise的结果
 * 1.promise和相同val,则reject,并提示循环调用
 * 2.如果val是promise,则继续val.then(resolve, reject)
 * 3.如果val是其他,则resolve(val)
 * @param {any} value
 * @param {Function} resolve
 * @param {Function} reject
 */
const resolvePromise = (promise, val, resolve, reject) => {
  if (promise === val) {
    return reject(
      new TypeError('Chaining cycle detected for promise #<Promise>')
    )
  }
  if (val instanceof MyPromise) {
    val.then(resolve, reject)
  } else {
    resolve(val)
  }
}

/** Class Promise的一个实现.以下简称MyPromise为Promise,promise代表Promise的实例 */
class MyPromise {
  /**
   * promise的执行器
   *
   * @Cb exCb
   * @param {Function} resolve
   * @param {Function} reject
   */
  /**
   * 创建一个Promise实例
   * @param {exCb} ex promise的执行器
   */
  constructor(ex) {
    try {
      ex(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }
  // promise的状态,默认为进行中
  status = PENDING
  // 成功后的值
  value = undefined
  // 失败的原因
  reason = undefined
  // 缓存的成功回调的数组
  fulfilledCbs = []
  // 缓存的失败回调的数组
  rejectedCbs = []
  /**
   * 更改promise状态为成功的方法
   * 使用箭头函数是为了保持方法调用时this指向是期望的指向 其他类似方法同理
   * @param {any} value 成功后的值
   */
  resolve = value => {
    if (this.status !== PENDING) {
      return
    }
    // 更改状态和值
    this.status = FULFILLED
    this.value = value
    // 调用成功回调如果可以的话
    while (this.fulfilledCbs.length) {
      this.fulfilledCbs.shift()()
    }
  }
  /**
   * 更改promise状态为失败的方法
   * @param {any} reason 失败的原因
   */
  reject = reason => {
    if (this.status !== PENDING) {
      return
    }
    // 更改状态和原因
    this.status = REJECTED
    this.reason = reason
    // 调用失败回调如果可以的话
    while (this.rejectedCbs.length) {
      this.rejectedCbs.shift()()
    }
  }
  /**
   * 原型对象的方法then
   * @param {Function} fulfilledCb 成功之后的回调
   * @param {Function} rejectedCb 失败之后的回调
   * @returns MyPromise
   */
  then(
    fulfilledCb = value => value,
    rejectedCb = reason => {
      throw reason
    }
  ) {
    // 返回一个promise 实现链式调用(then的返回值也应该是个promise)
    const promise2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        // 成功
        setTimeout(() => {
          // 捕获成功回调本身的错误
          try {
            // 这个value可能是个promise或者其他值
            const value = fulfilledCb(this.value)
            resolvePromise(promise2, value, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        // 成功
        setTimeout(() => {
          // 捕获失败回调本身的错误
          try {
            // 这个value可能是个promise或者其他值
            const reason = rejectedCb(this.reason)
            resolvePromise(promise2, reason, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      } else {
        // 当前处于等待状态
        // 缓存成功回调和失败回调
        this.fulfilledCbs.push(() => {
          setTimeout(() => {
            try {
              const value = fulfilledCb(this.value)
              resolvePromise(promise2, value, resolve, reject)
            } catch (error) {
              console.log('哈哈', error)
              reject(error)
            }
          }, 0)
        })
        this.rejectedCbs.push(() => {
          setTimeout(() => {
            try {
              const reason = rejectedCb(this.reason)
              resolvePromise(promise2, reason, resolve, reject)
            } catch (error) {
              console.log('哈哈', error)
              reject(error)
            }
          }, 0)
        })
      }
    })
    return promise2
  }
  /**
   * 多个promise异步并发的方法
   * @param {any|MyPromise} arr
   * @returns MyPromise
   */
  static all(arr) {
    const rs = []
    // 现有返回结果里元素的数量
    let rsNum = 0
    // 参数数组的长度
    const arrLen = arr.length
    return new MyPromise((resolve, reject) => {
      const addData = (value, index) => {
        rs[index] = value
        rsLen++
        // 这个时候arr里所有的promise状态都确定了
        if (rsNum === arrLen) {
          resolve(rs)
        }
      }
      arr.forEach((current, i) => {
        if (current instanceof MyPromise) {
          current.then(
            value => {
              addData(value, i)
            },
            reason => reject(reason)
          )
        } else {
          addData(current, i)
        }
      })
    })
  }
  /**
   * 静态的resolve方法 把任何值转化为一个promise对象
   * @param {any} value
   */
  static resolve(value) {
    if (value instanceof MyPromise) return value
    return new MyPromise(resolve => resolve(value))
  }
  /**
   * catch方法(特殊的then方法 只处理promise失败回调)
   * @param {Function} failCallback
   */
  catch(failCallback) {
    return this.then(undefined, failCallback)
  }
}

module.exports = MyPromise
